package pri._3d2;

import java.util.ArrayList;
import java.util.Collection;

/*
       z
       ↑/
    ---|--> y
      /|
     x
 */
public class Entity {
    /*
     * The datum point coordinates
     */
    private double x;
    private double y;
    private double z;

    /*
     * The toward directions. ●(datum) -->
     */
    private Double yz;
    private Double zx;
    private Double xy;

    /**
     * These are the related points to the datum point.
     */
    private Collection<RelatedPoint> points;


    /**
     * The World use this to draw. They are calculated by points.
     */
    private Collection<Point> worldPoints;

    // i 2: use points map
    // i 2: use lines map
    private Entity(double x, double y, double z) {
        this.x = x;
        this.y = y;
        this.z = z;
    }

    public Collection<Point> getWorldPoints() {
        return worldPoints;
    }

    // method to move

    // method to rotate

    /**
     * x, y, z are the coordinates<br/>
     * The generated Entity will toward Z direction after init<br/>
     * The Z direction is the default toward
     */
    public static EntityBuilder towardZ(double x, double y, double z) {
        return fromRadians(x, y, z, Math.PI / 2, 0D, null);
    }

    /**
     * x, y, z are the coordinates<br/>
     * yz, zx, xy art the radians in each datum plane
     */
    public static EntityBuilder fromRadians(double x, double y, double z,
                                     Double yz, Double zx, Double xy) {
        return new EntityBuilder(x, y, z, yz, zx, xy);
    }

    /**
     * x, y, z are the coordinates<br/>
     * yz, zx, xy art the degrees in each datum plane
     */
    public static EntityBuilder fromDegree(double x, double y, double z,
                                            Double yz, Double zx, Double xy) {
        return new EntityBuilder(x, y, z, Math.toRadians(yz), Math.toRadians(zx), Math.toRadians(xy));
    }

    private static class EntityBuilder {
        private final Entity entity;

        private EntityBuilder(double x, double y, double z,
                              Double yz, Double zx, Double xy) {
            entity = new Entity(x, y, z);
            entity.points = new ArrayList<>();
            entity.worldPoints = new ArrayList<>();

            entity.yz = yz;
            entity.zx = zx;
            entity.xy = xy;
        }

        /**
         * Add Point. All coordinates are related to the datum point.
         */
        public void ap(double x, double y, double z) {
            RelatedPoint rp = new RelatedPoint(entity, x, y, z);
            entity.points.add(rp);
            entity.worldPoints.add(rp.getWorldPoint());
        }

        public Entity end() {
            return entity;
        }
    }

    private static class RelatedPoint extends Point {

        /**
         * The datum point
         */
        private Entity entity;

        /**
         * The point in the world
         */
        private Point worldPoint;

        /*
         * The toward directions. Datum --> ●
         */
        private Double yz;
        private Double zx;
        private Double xy;

        /*
         * The distances
         */
        private double dis;
        private Double yzDis;
        private Double zxDis;
        private Double xyDis;

        /*
               z
               ↑/
            ---|--> y
              /|
             x

             ↑
             |    /|
             |  c  y
            -|/--x-|--->
             |
         */
        public RelatedPoint(Entity entity, double x, double y, double z) {
            super(x, y, z);
            this.entity = entity;

            yz = Math.atan2(z, y);
            zx = Math.atan2(x, z);
            xy = Math.atan2(y, x);

            dis = Math.pow(x, 2) + Math.pow(y, 2) + Math.pow(z, 2);
            yzDis = Math.pow(y, 2) + Math.pow(z, 2);
            zxDis = Math.pow(z, 2) + Math.pow(x, 2);
            xyDis = Math.pow(x, 2) + Math.pow(y, 2);

            worldPoint = new Point(0, 0, 0);
            // use origin xyz if entity toward is (pi/2, 0, null)
            // or else calculate the location
            resetWorldPoint();
        }

        private void resetWorldPoint() {
            // if entity toward default direction
            if (entity.yz.equals(Math.PI / 2) && entity.zx.equals(0D) && entity.xy == null) {
                worldPoint.setAll(entity.x + getX(), entity.y + getY(), entity.z + getZ());
                return;
            }
            // set new point by datum and radians
        }

        public Point getWorldPoint() {
            return worldPoint;
        }
    }
}
